/*
 * Copyright 2007-2008 Andrew O'Malley
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package ws.quokka.plugin.scala;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.Java;
import org.apache.tools.ant.util.FileUtils;

import java.io.File;
import java.io.IOException;

import java.net.InetAddress;
import java.net.Socket;


/**
 * CompileServerHelper can start and stop the compile server used by fsc.
 * It can also determine if the server is currently running and if so, the port it listening on
 */
public class CompileServerHelper {
    //~ Instance fields ------------------------------------------------------------------------------------------------

    private ScalaPlugin plugin;

    //~ Constructors ---------------------------------------------------------------------------------------------------

    public CompileServerHelper(ScalaPlugin plugin) {
        this.plugin = plugin;
    }

    //~ Methods --------------------------------------------------------------------------------------------------------

    /**
     * Returns the currently running fsc port, or -1 if it isn't running
     */
    public int findCompileServerPort() {
        int port = _findCompileServerPort();
        plugin.log().verbose("fsc compile server " + ((port == -1) ? "not detected" : ("detected on port " + port)));

        return port;
    }

    private int _findCompileServerPort() {
        String[] roots = new String[] { "scala.home:/var/scala-devel", "user.home:/tmp", "java.io.tmpdir:" };

        for (int i = 0; i < roots.length; i++) {
            String[] tokens = roots[i].split(":");
            String root = tokens[0];
            String path = tokens[1];

            if (System.getProperty(root) != null) {
                File rootDir = new File(System.getProperty(root));

                if (rootDir.isDirectory() && rootDir.canWrite()) {
                    // This is where fsc will write it's port file
                    return findCompileServerPort(FileUtils.getFileUtils().normalize(rootDir.getAbsolutePath() + path
                            + "/scalac-compile-server-port"));
                }
            }
        }

        return -1;
    }

    private int findCompileServerPort(File portDir) {
        if (!portDir.exists()) {
            return -1;
        }

        File[] files = portDir.listFiles();

        for (int i = 0; i < files.length; i++) {
            File file = files[i];
            int port;

            try {
                port = Integer.parseInt(file.getName());
            } catch (NumberFormatException e) {
                throw new BuildException("Invalid file in port temporary directory. Delete and try again: "
                    + file.getPath());
            }

            try {
                Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), port);

                try {
                    socket.getOutputStream();
                } finally {
                    socket.close();
                }

                return port; // Could connect, so return valid port
            } catch (IOException e) {
                // Continue to next file
            }
        }

        return -1;
    }

    public void startCompileServer() {
        plugin.log().info("Starting fsc compile server");

        Java server = plugin.createScalaCommand("compile-server");
        server.setClassname("scala.tools.nsc.MainGenericRunner");
        server.createArg().setValue("scala.tools.nsc.CompileServer");
        server.setSpawn(true);
        server.execute();
    }

    public void stopCompileServer() {
        plugin.log().info("Shutting down fsc compile server");
    }

    public void waitForCompileServer() {
        for (int i = 0; i < 100; i++) {
            int port = findCompileServerPort();

            if (port != -1) {
                return;
            }

            if (i == 0) {
                plugin.log().info("Waiting for compile server ...");
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // Won't be interrupted ...
            }
        }

        throw new BuildException("Timeout waiting for compile server to start has expired");
    }
}
